#include <stdlib.h>
#include <stdio.h>
#include <nds.h>

// common headers
#include "common.h"	// global

// local headers
#include "list.h"
#include "main.h"	// for 'exts'

#include "ini.h"

typedef struct {
	char *str;
	size_t len;
} sString;

void strstrip(sString *str) {
	if(!str || !str->str || !str->len) return;

	while(isspace(*str->str)) {
		str->str++;
		str->len--;
	}

	char *p=str->str+str->len-1;

	while(isspace(*p) && p>=str->str) {
		p--;
	}

	*(p+1)=0;

	str->len=p-str->str+1;
}

void line_rm_comment(sString *line) {
	if(!line || !line->str || !line->len) return;

	char *p=line->str;
	while(p-line->str<line->len) {
		if(iscomment(*p)) {
			*p=0;
			break;
		}

		p++;
	}

	line->len=p-line->str;
}

eLSTATUS line_status(sString line, sString *param, sString *value) {
	if(!param || !value) return L_ERROR;
	if(!line.str || !line.len) return L_EMPTY;

// get a copy of the line
	sString tmp;
	tmp.str=x->xmemdup(line.str, line.len+1);
	tmp.len=line.len;

	char *al=tmp.str;	// keep a pointer of allocated data to free it on exit

	line_rm_comment(&tmp);
	strstrip(&tmp);
	if(!tmp.len) {
		x->xfree(al);
		return L_EMPTY;
	}

	if(tmp.str[0]=='[' && tmp.str[tmp.len-1]==']') {
		tmp.str[tmp.len-1]=0;	tmp.len--;	// remove the ']'
		tmp.str++;				tmp.len--;	// remove the '['
		strstrip(&tmp);	// remove spaces before and after the section name
		if(!tmp.len) {
			x->xfree(al);
			return L_ERROR;
		}
		param->str=x->xmemdup(tmp.str, tmp.len+1);
		param->len=tmp.len;

		x->xfree(al);
		return L_SECTION;
	}

	char *p=std->strchr(tmp.str, '=');
	if(!p || /*p != std->strrchr(tmp.str, '=') ||*/ p==tmp.str) {
		x->xfree(al);
		return L_ERROR;
	}

	*p=0;

	// get param
	param->str=tmp.str;
	param->len=p-tmp.str;
	strstrip(param);
	param->str=x->xmemdup(param->str, param->len+1);

	p++;

	// get value
	value->str=p;
	value->len=tmp.str+tmp.len-p;
	strstrip(value);
	value->str=x->xmemdup(value->str, value->len+1);

	x->xfree(al);
	return L_ASSIGNMENT;
}

void *open(FILE *fd, u8 idx_ext) {
	if(!fd || f->error(fd) || idx_ext>=exts.extnum) return NULL;

//std->iprintf("libini: opening \"%s\" file\n", exts.exts[idx_ext]);

	f->rewind(fd);

	sINI *ini=(sINI *)x->xcalloc(1, sizeof(sINI));

	sString line, param, value;
	size_t allocd;
	u32 numline=0;
	char *section=NULL;

// read size
#define RSIZE (32)
// block size
#define BSIZE (32*3)

	while(1) {
		std->bzero(&line, sizeof(sString));
		allocd=0;

		while(1) {	// get entire line
			if(line.len+RSIZE>allocd) {	// allocate enough memory
				allocd+=BSIZE;
				line.str=(char *)x->xrealloc(line.str, allocd);
			}

			f->gets(line.str+line.len, RSIZE, fd);

			if(f->error(fd)) {
				x->xfree(ini);
				x->xfree(line.str);
				return NULL;
			}

			if(f->eof(fd)) {
				break;
			}

			line.len+=std->strlen(line.str+line.len);	// very bad to strlen() !!!

			if(line.str[line.len-1]=='\n') break;
		}
		line.str=(char *)x->xrealloc(line.str, line.len+1);	// keep allocated only the used memory
		line.str[line.len]=0;
		numline++;

		std->bzero(&param, sizeof(sString));
		std->bzero(&value, sizeof(sString));

		switch(line_status(line, &param, &value)) {
		case L_SECTION:
//			std->iprintf("libini: section \"[%s]\" found\n", param.str);
			if(section) x->xfree(section);
			section=param.str;
			break;
		case L_ASSIGNMENT:
//			std->iprintf("libini: assignment \"%s=%s\" found\n", param.str, value.str);
			il_add(ini, x->xstrdup(section), x->xstrdup(param.str), x->xstrdup(value.str));
			break;
		case L_ERROR:
			std->iprintf("libini: error: line %d: \"%s\"(%d)\n", numline, line.str, line.len);
		case L_EMPTY:
		case L_COMMENT:
		default:
			break;
		}

		if(param.str && section!=param.str) x->xfree(param.str);
		if(value.str) x->xfree(value.str);
		x->xfree(line.str);

		if(f->eof(fd)) break;
	}

	if(section) x->xfree(section);

	return (void *)ini;
}

char *bool_test[]= {
	"y",	"n",
	"1",	"0",
	"yes",	"no",
	"true",	"false",
	NULL
};

int get_bool(void *confd, char *section, char *param, bool *value) {
	if(!confd || !section || !param || !value) return 1;

	char *sval;

	int ret;

	ret=il_get((sINI *)confd, section, param, &sval);

	if(ret || !sval) return 1;

	char **p=bool_test;
	while(1) {
		if(!std->strcasecmp(*p, sval)) {
			*value=true;
			break;
		}
		if(!*(++p)) return 1;

		if(!std->strcasecmp(*p, sval)) {
			*value=false;
			break;
		}
		if(!*(++p)) return 1;
	}

	return 0;
}

int get_integer(void *confd, char *section, char *param, s32 *value) {
	if(!confd || !section || !param || !value) return 1;

	char *sval;

	int ret;

	ret=il_get(confd, section, param, &sval);

	if(ret || !sval) return 1;

	*value=std->atoi(sval);

	return 0;
}

int get_float(void *confd, char *section, char *param, float *value) {
	if(!confd || !section || !param || !value) return 1;

	char *sval;

	int ret;

	ret=il_get(confd, section, param, &sval);

	if(ret || !sval) return 1;

	*value=std->atof(sval);

	return 0;
}

int set_string(void *confd, char *section, char *param, char *value) {
	if(!confd || !section || !param || !value) return 1;

	// TODO

	return 1;
}

int set_bool(void *confd, char *section, char *param, bool value) {
	if(!confd || !section || !param) return 1;

	// TODO

	return 1;
}

int set_integer(void *confd, char *section, char *param, s32 value) {
	if(!confd || !section || !param) return 1;

	// TODO

	return 1;
}

int set_float(void *confd, char *section, char *param, float value) {
	if(!confd || !section || !param) return 1;

	// TODO

	return 1;
}

int unset(void *confd, char *section, char *param) {
	if(!confd || !section || !param) return 1;

	// TODO

	return 1;
}

int save(void *confd, FILE *fd) {
	if(!confd || !fd) return 1;

	// TODO

	return 1;
}

void close(void *confd) {
	if(!confd) return;

	il_removeAll((sINI *)confd, il_removeFree_h);
	x->xfree(confd);
}

